package com.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import cn.hutool.json.JSONUtil;
import com.consts.GenerateConsts;
import com.entity.generate.FkBean;
import com.entity.generate.GenerateParameters;
import com.entity.generate.Table;
import com.entity.generate.TypeUtil;

import java.nio.charset.Charset;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

/**
 * @ClassName GeneratorUtils
 * @Description TODO
 * @Author 谭庆伟
 * @Date 2020/8/22 14:24
 * @Version 1.0
 **/
@SuppressWarnings("ALL")
public class Generator1 {

    private GenerateParameters parameters = new GenerateParameters();
    private MetaUtil metaUtil;
    //public static final String resourcesPath = "D:\\data\\idea\\sys-manager\\sys-manager\\src\\main\\resources\\templates\\";
    public Generator1(MetaUtil metaUtil,String packageName,String dbName,String tableName, String resourcesPath, String writePath,String vuePath) {
        this.parameters.setPackageName(packageName);
        Table table = metaUtil.getTableMeta(dbName,tableName);
        this.metaUtil = metaUtil;
        this.parameters.setTableName(tableName) ;
        this.parameters.setDbName(dbName);
        String className = StrUtil.toCamelCase("_" + tableName);
        String camelCaseClassName = StrUtil.toCamelCase( tableName);
        this.parameters.setClassName(className);
        this.parameters.setCamelCaseClassName(camelCaseClassName);
        this.parameters.setTableColumns(table.getColumns());
        this.parameters.setTableComment(table.getComment());
        Set<String> pkNames = table.getPkNames();
        this.parameters.setFkList(table.getFkList());
        this.parameters.setPkName(pkNames.iterator().next());
        this.parameters.setPkNameCamelCase(StrUtil.toCamelCase(this.parameters.getPkName()));

        this.parameters.setResourcesPath(resourcesPath);
        this.parameters.setWritePath(writePath);
        this.parameters.setVuePath(vuePath);


        getAllParams();
    }

    private void getAllParams() {
        HashMap<String, Object> map = new HashMap<>();
        //用于inserts的foreach
        StrBuilder noKeyColumnsValWithMap = new StrBuilder();
        StrBuilder queryAll = new StrBuilder();//mapper queryAll查询
        StrBuilder tableIf = new StrBuilder();//mapper tableIf
        StrBuilder tableIfWithTableName = new StrBuilder();


        StrBuilder columnAlias = new StrBuilder();//列别名
        StrBuilder columnAliasWithTableName= new StrBuilder();;

        StrBuilder noKeyColumns = new StrBuilder();
        StrBuilder noKeyColumnsVal = new StrBuilder();
        StrBuilder updateIf = new StrBuilder();

        //前端start
        StrBuilder ruleJson = new StrBuilder();
        StrBuilder tableHeader = new StrBuilder();
        tableHeader.append("[");

        StrBuilder indexSerch = new StrBuilder();
        StrBuilder classNameJson = new StrBuilder();
        classNameJson.append("{");

        StrBuilder addItems = new StrBuilder();
        //前端end
        Collection<Column> tableColumns = this.parameters.getTableColumns();
        String tableName = this.parameters.getTableName();
        int i=0;
        ruleJson.append("{");

        for (Column tableColumn : tableColumns) {
            i++;
            //将不是主键的列加入进来
            String columnName = tableColumn.getName();
            String columnComment = tableColumn.getComment();
            String columnNameCamelCase = StrUtil.toCamelCase(columnName);//列名小驼峰

            String tableColumnNameCamelCase = StrUtil.toCamelCase(tableName+"_"+columnName);//表名+列名小驼峰

            StrBuilder tableIfBuilder = new StrBuilder();
            StrBuilder tableIfWithTableNameBuilder = new StrBuilder();


            StrBuilder updateIfBuilder = new StrBuilder();
            if(!this.parameters.getPkName().equals(columnName)){
                noKeyColumnsValWithMap.append("#{" +"map."+ StrUtil.toCamelCase(columnName)+"},");
                noKeyColumns.append(columnName+",");

                noKeyColumnsVal.append("#{" + StrUtil.toCamelCase(columnName)+"},");

                String tableHeaderStr = "{title: \""+columnComment+"\", dataIndex: \""+columnNameCamelCase+"\", width: 200,},";
                tableHeader.append(tableHeaderStr);

                String indexSerchStr = "<a-col :md=\"6\" :sm=\"24\"><a-form-model-item prop=\""+columnNameCamelCase+"\" label=\""+columnComment+"\" :labelCol=\"{span: 5}\" :wrapperCol=\"{span: 18, offset: 1}\"><a-input placeholder=\"请输入"+columnComment+"\" v-model=\""+this.parameters.getCamelCaseClassName()+"."+columnNameCamelCase+"\" /></a-form-model-item></a-col>";
                indexSerch.append(indexSerchStr);

                String addItemsStr = "<a-form-model-item label=\""+tableColumn.getComment()+"\" prop=\""+columnNameCamelCase+"\"><a-input v-model=\""+this.parameters.getCamelCaseClassName()+"."+ columnNameCamelCase +"\" /></a-form-model-item>";
                addItems.append(addItemsStr);
            }else {
                //主键处理
                String tableHeaderStr = "{title: \""+columnComment+"\", dataIndex: \""+columnNameCamelCase+"\", width: 200,scopedSlots: { customRender: '"+columnNameCamelCase+"' }},";
                tableHeader.append(tableHeaderStr);
            }

            tableIfBuilder.append("<if test=\""+columnNameCamelCase+" != null  and "+columnNameCamelCase+" != ''\">");
            tableIfBuilder.append("and "+columnName+" = #{"+columnNameCamelCase+"}");
            tableIfBuilder.append("</if>");
            tableIf.append(tableIfBuilder);

            tableIfWithTableNameBuilder.append("<if test=\""+tableColumnNameCamelCase+" != null  and "+tableColumnNameCamelCase+" != ''\">");
            tableIfWithTableNameBuilder.append("and "+tableName+"."+columnName+" = #{"+tableColumnNameCamelCase+"}");
            tableIfWithTableNameBuilder.append("</if>");
            tableIfWithTableName.append(tableIfWithTableNameBuilder);




            columnAlias.append(columnName +" "+columnNameCamelCase+",");
            columnAliasWithTableName.append(tableName + "." +columnName +" "+tableColumnNameCamelCase+",");

            updateIfBuilder.append("<if test=\""+columnNameCamelCase+" != null  and "+columnNameCamelCase+" != ''\">");
            if(i==tableColumns.size()){
                updateIfBuilder.append(" "+columnName+" = #{"+columnNameCamelCase+"}");
            }else {
                updateIfBuilder.append(" "+columnName+" = #{"+columnNameCamelCase+"},");
            }
            updateIfBuilder.append("</if>");
            updateIf.append(updateIfBuilder);


            String ruleJsonStr = ""+columnNameCamelCase+": [{required: true,message: \"请输入"+columnComment+"\",trigger: \"blur\",}],";
            ruleJson.append(ruleJsonStr);





            //String classNameJsonStr = ""+columnName+": '"+"',";
            String classNameJsonStr = ""+columnNameCamelCase+": '"+"',";
            classNameJson.append(classNameJsonStr);



        }
        this.parameters.setAddItems(addItems.toString());
        classNameJson.append("}");
        this.parameters.setClassNameJson(JSONUtil.formatJsonStr(classNameJson.toString()));


        this.parameters.setIndexSerch(JSONUtil.formatJsonStr(indexSerch.toString()));

        tableHeader.append("{title: \"操作\",scopedSlots: { customRender: \"action\" },width: 200,},]");
        this.parameters.setTableHeader(JSONUtil.formatJsonStr(tableHeader.toString()));


        ruleJson.append("}");
        this.parameters.setRuleJson(ruleJson.toString());
        this.parameters.setUpdateIf(updateIf.toString());
        String noKeyColumnsValStr = noKeyColumnsVal.del(noKeyColumnsVal.length() - 1, noKeyColumnsVal.length()).toString();
        this.parameters.setNoKeyColumnsVal(noKeyColumnsValStr);
        String noKeyColumnsStr = noKeyColumns.del(noKeyColumns.length() - 1, noKeyColumns.length()).toString();
        this.parameters.setNoKeyColumns(noKeyColumnsStr);
        String columnAliasStr = columnAlias.del(columnAlias.length() - 1, columnAlias.length()).toString();
        this.parameters.setColumnAlias(columnAliasStr);

        String columnAliasWithTableNameStr = columnAliasWithTableName.del(columnAliasWithTableName.length() - 1, columnAliasWithTableName.length()).toString();
        this.parameters.setColumnAliasWithTable(columnAliasWithTableNameStr);


        //start
        this.parameters.setTableIf(tableIf.toString());
        this.parameters.setTableIfWithTableName(tableIfWithTableName.toString());
        //end


        //start
        queryAll.append("select ");
        queryAll.append(this.parameters.getColumnAlias());
        queryAll.append(" from ");
        queryAll.append(this.parameters.getTableName());
        queryAll.append(" <where>");
        queryAll.append(this.parameters.getTableIf());
        queryAll.append(" </where>");
        //end
        this.parameters.setQueryAll(queryAll.toString());

        noKeyColumnsValWithMap = noKeyColumnsValWithMap.del(noKeyColumnsValWithMap.length() - 1, noKeyColumnsValWithMap.length());
        this.parameters.setNoKeyColumnsValWithMap(noKeyColumnsValWithMap.toString());

        if(this.parameters.getFkList().size()>0){
            String queryAllJoin = getQueryAllJoin();
            queryAllJoin = "<select id=\"queryAllJoin\" resultType=\"map\">"+queryAllJoin+"</select>";
            this.parameters.setQueryAllJoin(queryAllJoin);
        }else {
            this.parameters.setQueryAllJoin("");
        }
    }

    private String getQueryAllJoin() {
        //拼接sql语句
        //select * from tableName left join
        StrBuilder allColumn = new StrBuilder();
        allColumn.append(this.parameters.getColumnAliasWithTableName()+",");
        StrBuilder allLeftJoin = new StrBuilder();
        StrBuilder allWhere = new StrBuilder();
        StrBuilder allIf = new StrBuilder();
        allWhere.append("<where>");
        String tableName = this.parameters.getTableName();
        List<FkBean> fkList = this.parameters.getFkList();
        String resourcesPath = this.parameters.getResourcesPath();
        String writePath = this.parameters.getWritePath();
        String vuePath = this.parameters.getVuePath();
        for (int j = 0; j < fkList.size(); j++) {
            FkBean fkBean = fkList.get(j);
            String referencesTableName = fkBean.referencesTableName;
            Generator1 generator = new Generator1(this.metaUtil, parameters.getPackageName(), parameters.getDbName(), referencesTableName, resourcesPath, writePath, vuePath);
            String referencesColumnName = fkBean.getReferencesColumnName();
            String fkColumnName = fkBean.getFkColumnName();
            allColumn.append(generator.getParameters().getColumnAlias()+",");

            allIf.append(generator.getParameters().getTableIf());
            allLeftJoin.append(" left join " + referencesTableName + " on " + referencesTableName + "." + referencesColumnName + " = " + tableName + "." + fkColumnName);
        }
        allWhere.append(allIf);
        allWhere.append("</where>");
        String columnsStr = allColumn.del(allColumn.length() - 1, allColumn.length()).toString();
        String res = "select " + columnsStr+" from " + tableName + allLeftJoin + allWhere;
        return res;
    }


    public void createController() {
        String entityPath = this.parameters.getResourcesPath() + "controller" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getClassName());
        map.put("camelCaseClassName", this.parameters.getCamelCaseClassName());
        String apiOperationSupport = getApiOperationSupport();
        //String apiDelOperationSupport = getDelApiOperationSupport();
        map.put("queryAllApiOperationSupport", apiOperationSupport);
        map.put("queryAllJoinApiOperationSupport", apiOperationSupport);
        map.put("queryByIdApiOperationSupport", apiOperationSupport);
        map.put("insertApiOperationSupport", apiOperationSupport);
        map.put("insertsApiOperationSupport", apiOperationSupport);
        map.put("updateApiOperationSupport", apiOperationSupport);
        map.put("deleteByIdsApiOperationSupport", "");
        map.put("deleteByIdApiOperationSupport", "");
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), this.parameters.getWritePath() + "controller\\" + this.parameters.getClassName() + "Controller.java", Charset.defaultCharset());
    }


    public void createService() {

        String entityPath = this.parameters.getResourcesPath() + "service" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getClassName());
        map.put("context", columnsGetSet());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), this.parameters.getWritePath() + "service\\" + this.parameters.getClassName() + "Service.java", Charset.defaultCharset());

    }
    public void createServiceImpl() {

        String entityPath = this.parameters.getResourcesPath() + "serviceImpl" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getClassName());
        map.put("camelCaseClassName",this.parameters.getCamelCaseClassName() );
        map.put("pkNameCamelCase",this.parameters.getPkNameCamelCase());
        String format = StrUtil.format(templetaEntityStr, map);

        FileUtil.writeString(JavaForm.formJava(format), this.parameters.getWritePath() + "service\\impl\\" + this.parameters.getClassName() + "ServiceImpl.java", Charset.defaultCharset());

    }
    public void createField() {
        String entityPath = this.parameters.getResourcesPath() + "field" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getClassName());
        map.put("context", getField());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), this.parameters.getWritePath() + "entity\\field\\" + this.parameters.getClassName() + "Field.java", Charset.defaultCharset());
    }
    public String getField(){
        StrBuilder builder = new StrBuilder();
        String className = this.parameters.getClassName();
        for (Column column : this.parameters.getTableColumns()) {
            String columnName = column.getName();
            int type = column.getType();
            String javaType = TypeUtil.getJavaType(type);
            String field = StrUtil.toCamelCase(columnName);
            builder.append("public static final " + "String" + " " + columnName.toUpperCase()+" = \"" + field+"\";\n" );
        }
        return builder.toString();
    }
    public void createEntity() {
        String entityPath = this.parameters.getResourcesPath() + "entity" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getClassName());
        map.put("context", columnsGetSet());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), this.parameters.getWritePath() + "entity\\" + this.parameters.getClassName() + "Entity.java", Charset.defaultCharset());
    }

    public void createDao() {

        String entityPath = this.parameters.getResourcesPath() + "dao" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getClassName());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), this.parameters.getWritePath() + "dao\\" + this.parameters.getClassName() + "Dao.java", Charset.defaultCharset());

    }

    public void createIndexJs() {
        String entityPath = this.parameters.getResourcesPath() + "indexJs" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getCamelCaseClassName());
        String format = StrUtil.format(templetaEntityStr, map);
        //String path = FileUtil.getParent(writePath, 3) + "\\resources\\public\\vue-antd-admin\\src\\pages\\"+className+"\\";
        FileUtil.writeString(format, this.parameters.getVuePath()+"\\src\\services\\"+ this.parameters.getCamelCaseClassName() + ".js", Charset.defaultCharset());
    }

    public void createAdd() {
        String entityPath = this.parameters.getResourcesPath() + "add" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getCamelCaseClassName());
        map.put("addItems", this.parameters.getAddItems());
        map.put("pkNameCamelCase", this.parameters.getPkNameCamelCase());
        map.put("classNameJson", this.parameters.getClassNameJson());
        map.put("rulesJson", this.parameters.getRuleJson());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(HtmlFormat.format(format), this.parameters.getVuePath()+"\\src\\pages\\"+this.parameters.getCamelCaseClassName()+"\\"+ this.parameters.getCamelCaseClassName() + "Add.vue", Charset.defaultCharset());
    }

    public void createEdit() {
        String entityPath = this.parameters.getResourcesPath() + "edit" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getCamelCaseClassName());
        String addItems = this.parameters.getAddItems();
        map.put("addItems", addItems);
        map.put("pkNameCamelCase", this.parameters.getPkNameCamelCase());
        map.put("classNameJson", this.parameters.getClassNameJson());
        map.put("rulesJson", this.parameters.getRuleJson());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(HtmlFormat.format(format), this.parameters.getVuePath()+"\\src\\pages\\"+this.parameters.getCamelCaseClassName()+"\\"+ this.parameters.getCamelCaseClassName() + "Edit.vue", Charset.defaultCharset());
    }

    public void createIndex() {
        String entityPath = this.parameters.getResourcesPath() + "index" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getCamelCaseClassName());
        map.put("serchItem", this.parameters.getIndexSerch());
        map.put("pkNameCamelCase", this.parameters.getPkNameCamelCase());
        map.put("tableHeader", this.parameters.getTableHeader());
        map.put("classNameJson", this.parameters.getClassNameJson());
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(HtmlFormat.format(format), this.parameters.getVuePath()+"\\src\\pages\\"+this.parameters.getCamelCaseClassName()+"\\"+  this.parameters.getCamelCaseClassName() + "Index.vue", Charset.defaultCharset());
    }

    /**
     * 在config.js中加入文本列表
     */
    public void addConfigJsNext() {
        String templetaPath = this.parameters.getResourcesPath() + "vueConfigJsNext" + ".txt";
        String templetaStr = FileUtil.readUtf8String(templetaPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getCamelCaseClassName());
        map.put("tableComment", this.parameters.getTableComment());
        String format = StrUtil.format(templetaStr, map);
        GenerateConsts.VUECONFIGJSNEXTBUILDER.append(format);
    }

    /**
     * 在config.js中加入文本列表
     */
    public void addRouterMapJsNext() {
        String templetaPath = this.parameters.getResourcesPath() + "routerMapJsNext" + ".txt";
        String templetaStr = FileUtil.readUtf8String(templetaPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", this.parameters.getCamelCaseClassName());
        String format = StrUtil.format(templetaStr, map);
        GenerateConsts.ROUTERMAPJSNEXTBUILDER.append(format);
    }


    public void createMapper() {

        String entityPath = this.parameters.getResourcesPath() + "mapper" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();

        map.put("className", this.parameters.getClassName());
        map.put("tableName", this.parameters.getTableName());
        map.put("columnAlias", this.parameters.getColumnAlias());
        map.put("pkName", this.parameters.getPkName());
        map.put("pkNameCamelCase", this.parameters.getPkNameCamelCase());
        map.put("queryAllJoin",this.parameters.getQueryAllJoin());
        map.put("queryAll", this.parameters.getQueryAll());
        map.put("noKeyColumns", this.parameters.getNoKeyColumns());
        map.put("noKeyColumnsVal", this.parameters.getNoKeyColumnsVal());
        map.put("updateIf", this.parameters.getUpdateIf());
        map.put("noKeyColumnsValWithMap", this.parameters.getNoKeyColumnsValWithMap());

        String format = StrUtil.format(templetaEntityStr, map);
        String path = FileUtil.getParent(this.parameters.getWritePath(), 3) + "\\resources\\";
        try {
            FileUtil.writeString(format,path + "mapper\\" + this.parameters.getClassName() + "Mapper.xml", Charset.defaultCharset());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    private String columnsGetSet() {

        StrBuilder builder = new StrBuilder();
        for (Column column : this.parameters.getTableColumns()) {
            String columnName = column.getName();
            int type = column.getType();
            String javaType = TypeUtil.getJavaType(type);
            String columnCamelCase = StrUtil.toCamelCase(columnName);//
            String columnCamelCaseGetSet = StrUtil.toCamelCase("_" + columnName);//
            builder.append(columnsIn(javaType, columnCamelCase));
            builder.append(columnsSet(javaType, columnCamelCase, columnCamelCaseGetSet));
            builder.append(columnsGet(javaType, columnCamelCase, columnCamelCaseGetSet));
        }

        return builder.toString();
    }

    private String columnsIn(String javaType, String columnCamelCase) {
        StrBuilder builder = new StrBuilder();
        builder.append("private " + javaType + " " + columnCamelCase + ";\n");
        return builder.toString();
    }

    private String columnsSet(String javaType, String columnCamelCase, String columnCamelCaseGetSet) {
        StrBuilder builder = new StrBuilder();
        builder.append("public void set" + columnCamelCaseGetSet + "(" + javaType + " " + columnCamelCase + "){this." + columnCamelCase + "=" + columnCamelCase + ";}\n");
        return builder.toString();
    }

    private String columnsGet(String javaType, String columnCamelCase, String columnCamelCaseGetSet) {
        StrBuilder builder = new StrBuilder();
        builder.append("public " + javaType + " get" + columnCamelCaseGetSet + "(){return this." + columnCamelCase + ";}\n");
        return builder.toString();
    }

    private String ObjectSet(String type, String var, String objectName) {
        StrBuilder builder = new StrBuilder();
        builder.append("public void set" + objectName + "(" + type + " " + var + "){this." + var + "=" + var + ";}\n");
        return builder.toString();
    }

    private String ObjectGet(String type, String var, String objectName) {
        StrBuilder builder = new StrBuilder();
        builder.append("public " + type + " get" + objectName + "(){return this." + var + ";}\n");
        return builder.toString();
    }


    public GenerateParameters getParameters() {
        return parameters;
    }

    public String getApiOperationSupport() {
        StrBuilder builder = new StrBuilder();
        Collection<Column> tableColumns = this.parameters.getTableColumns();
        for (Column column : tableColumns) {
            String name = StrUtil.toCamelCase(column.getName());
            String comment = column.getComment();
            builder.append("@DynamicParameter(name = \""+name+"\", value = \""+comment+"\"),\n");
        }
        //@DynamicParameter(name = "name", value = "名字"),
        return builder.toString();
    }
}
